package com.swak.consistency.tcc.role;

import java.lang.reflect.Method;
import java.util.List;

import com.swak.App;
import com.swak.annotation.Tcc;
import com.swak.consistency.core.aspect.ConsistencyProceeding;
import com.swak.consistency.core.context.ContextBindAttr;
import com.swak.consistency.core.execption.ConsistencyException;
import com.swak.consistency.core.invoke.Invocation;
import com.swak.consistency.core.invoke.Invoker;
import com.swak.consistency.core.logger.ConsistencyLogger;
import com.swak.consistency.tcc.context.Phase;
import com.swak.consistency.tcc.context.TccContext;
import com.swak.consistency.tcc.context.TransLog;
import com.swak.consistency.tcc.context.TransParticipant;
import com.swak.consistency.tcc.storage.TransLogStorage;
import com.swak.utils.Lists;
import com.swak.utils.StringUtils;
import com.swak.utils.time.DateTimes;

public abstract class AbstractTransRoler implements TransRoler {

	protected TransLogStorage transLogStorage;
	protected Invoker invoker;

	public void init(TransLogStorage transLogStorage, Invoker invoker) {
		this.transLogStorage = transLogStorage;
		this.invoker = invoker;
	}

	/**
	 * 开始事务
	 * 
	 * @param context
	 * @return 是否需要执行接下来 Prepare，返回 true 则是需要执行
	 */
	protected void openGlobalTrans(TccContext context) {

		ConsistencyLogger.debug(context, "开启全局事务");

		// 当前的事务参数
		Long transId = context.getTransId();

		// 创建本地事务
		TransLog trans = new TransLog();
		trans.setTransId(transId == null ? App.genId() : transId);
		trans.setPhase(Phase.TRYED.getCode());
		trans.setRole(context.getRole().getCode());
		trans.setRetryTimes(0);
		trans.setVersion(0);

		ConsistencyProceeding proceeding = context.getProceeding();
		Class<?> clazz = proceeding.getTarget();
		Method method = proceeding.getMethod();

		trans.setTargetClass(clazz.getName());
		trans.setTargetMethod(method.getName());
		trans.setCreateTime(DateTimes.now());
		trans.setUpdateTime(DateTimes.now());

//		// 添加事务第二阶段方法
//		Object[] args = proceeding.getArgs();
//		Tcc tcc = method.getAnnotation(Tcc.class);
//		Invocation confirmInvocation = null;
//		Invocation cancelInvocation = null;
//		String confirmMethodName = tcc.confirm();
//		if (StringUtils.isNotBlank(confirmMethodName)) {
//			confirmInvocation = invoker.createInvocation(clazz, method, args, confirmMethodName);
//		}
//		String cancelMethodName = tcc.cancel();
//		if (StringUtils.isNotBlank(cancelMethodName)) {
//			cancelInvocation = invoker.createInvocation(clazz, method, args, cancelMethodName);
//		}
//		TransParticipant participant = new TransParticipant();
//		participant.setConfirm(confirmInvocation);
//		participant.setCancel(cancelInvocation);
//		trans.addParticipant(participant);

		// 保存事务 -- 事务不成功直接抛出异常
		this.transLogStorage.create(trans);

		// 设置当前事务上下文
		context.update(trans, Phase.READY);
	}

	/**
	 * 开始分支事务
	 * 
	 * @param context
	 */
	protected TransParticipant openBranchTrans(TccContext context) {

		ConsistencyLogger.debug(context, "开启分支事务");

		TransLog trans = context.getTrans();
		ConsistencyProceeding proceeding = context.getProceeding();
		Method method = proceeding.getMethod();
		Tcc tcc = method.getAnnotation(Tcc.class);

		// 添加事务第二阶段方法
		Invocation confirmInvocation = null;
		Invocation cancelInvocation = null;
		String confirmMethodName = tcc.confirm();
		if (StringUtils.isNotBlank(confirmMethodName)) {
			confirmInvocation = invoker.createInvocation(proceeding, confirmMethodName);
		}
		String cancelMethodName = tcc.cancel();
		if (StringUtils.isNotBlank(cancelMethodName)) {
			cancelInvocation = invoker.createInvocation(proceeding, cancelMethodName);
		}
		TransParticipant participant = new TransParticipant();
		participant.setConfirm(confirmInvocation);
		participant.setCancel(cancelInvocation);
		trans.addParticipant(participant);

		// 保存事务 -- 事务不成功直接抛出异常
		transLogStorage.updateParticipants(trans);

		return participant;
	}

	/**
	 * 执行准备阶段
	 * 
	 * @param context
	 * @return
	 */
	protected Object doPrepare(TccContext context) {

		ConsistencyLogger.debug(context, "执行准备阶段");

		Throwable error = null;
		Object result = null;
		try {
			result = context.getProceeding().proceed();
		} catch (Throwable e) {
			error = e;
		}

		ConsistencyLogger.debug(context, "执行准备阶段 " + (error == null ? "成功" : "失败"));

		// 抛出异常
		if (error != null) {
			return this.throwConsistencyException(error);
		}

		// 返回结果
		return result;
	}

	/**
	 * 准备阶段的后置处理器
	 */
	protected void postGlobalTrans(TccContext context) {

		// 确定try阶段的执行成功
		TransLog trans = context.getTrans();
		trans.setPhase(Phase.TRYED.getCode());
		this.transLogStorage.updatePhase(trans);

		// 设置当前事务上下文
		context.update(trans, Phase.TRYED);

		ConsistencyLogger.debug(context, "确定全局事务");
	}

	/**
	 * 准备阶段的后置处理器
	 */
	protected void postBranchTrans(TccContext context, TransParticipant branch) {
		TransLog trans = context.getTrans();
		branch.setContext(context.getBindAttr().isEmpty() ? null : context.getBindAttr());
		branch.setExecuted(Boolean.TRUE);
		this.transLogStorage.updateParticipants(trans);

		ConsistencyLogger.debug(context, "确定分支事务");
	}

	/**
	 * 确认事务
	 * 
	 * @param context
	 */
	protected void doConfirm(TccContext context) {
		Throwable error = null;
		boolean confirmPhaseResult = true;

		// 只要进入确认阶段， 需保证所有的参与者都执行成功，如果不能成功则需要人工的干预
		try {
			// 当前事务
			TransLog trans = this.transLogStorage.get(context.getTransId());

			// 默认就是提交阶段
			trans.setPhase(Phase.CONFIRMING.getCode());
			this.transLogStorage.updatePhase(trans);

			// 设置当前事务上下文
			context.update(trans, Phase.CONFIRMING);

			// 执行确认
			confirmPhaseResult = this.invokeParticipants(context, trans);

		} catch (Throwable e) {
			error = e;
			ConsistencyLogger.error(context, "确认阶段异常，部分参与者确认阶段未执行成功，后续开启异步重试，", e);
			// ignore
		}

		if (ConsistencyLogger.isDebugEnabled() && error == null && confirmPhaseResult) {
			ConsistencyLogger.debug(context, "确认阶段执行成功");
		} else if (ConsistencyLogger.isDebugEnabled() && !confirmPhaseResult) {
			ConsistencyLogger.debug(context, "确认阶段异常，部分参与者确认阶段未执行成功，后续开启异步重试，");
		}

		// 确认阶段需要抛出异常， 发起者不用处理，但是事务提供者需要将异常抛出器，抛给事务发起者
		if (error != null) {
			this.throwConsistencyException(error);
		}
	}

	/**
	 * 是否全部执行成功
	 * 
	 * @param context 事务上下文
	 * @param trans   事务对象
	 * @return
	 */
	protected boolean invokeParticipants(TccContext context, TransLog trans) {
		List<TransParticipant> fails = Lists.newArrayList();
		List<TransParticipant> participants = trans.getParticipants();
		if (participants != null && !participants.isEmpty()) {
			for (TransParticipant participant : participants) {
				if (!this.invokeInvocation(context, participant)) {
					fails.add(participant);
				}
			}
		}

		boolean success = !(fails != null && fails.size() > 0);
		if (!success) {
			trans.setParticipants(fails);
			this.transLogStorage.updateParticipants(trans);
		} else {
			this.transLogStorage.remove(trans);
		}
		return success;
	}

	/**
	 * 执行 TransInvocation
	 * 
	 * @param invocation
	 * @throws Exception
	 */
	protected boolean invokeInvocation(TccContext context, TransParticipant participant) {

		// try 阶段必须执行成功才能执行后续的confirm 或者 cancel
		if (participant == null || participant.getExecuted() == null) {
			return true;
		}

		// 事务的执行阶段
		Phase phase = context.getPhase();

		// 事务执行阶段
		Invocation invocation = phase == Phase.CONFIRMING ? participant.getConfirm() : participant.getCancel();

		// 不需要执行相应的阶段
		if (invocation == null) {
			return true;
		}

		// 切换执行的上下文，将实际的参数往下传递
		ContextBindAttr oldBindAttr = context.getBindAttr();
		try {
			context.setBindAttr(participant.getContext());
			invoker.invokeInvocation(invocation);
		} catch (Throwable e) {
			ConsistencyLogger.error(context, "第二阶段执行异常：" + invocation + "; 事务阶段：", e);
			return false;
		} finally {
			context.setBindAttr(oldBindAttr);
		}
		return true;
	}

	/**
	 * 确认事务
	 * 
	 * @param context
	 */
	protected void doCancel(TccContext context) {
		Throwable error = null;
		boolean cancelPhaseResult = true;

		// 只要进入取消阶段， 需保证所有的参与者都执行成功，如果不能成功则需要人工的干预
		try {
			// 当前事务
			TransLog trans = this.transLogStorage.get(context.getTransId());

			// 默认就是提交阶段
			trans.setPhase(Phase.CANCELING.getCode());
			this.transLogStorage.updatePhase(trans);

			// 设置当前事务上下文
			context.update(trans, Phase.CANCELING);

			// 执行确认
			cancelPhaseResult = this.invokeParticipants(context, trans);

		} catch (Throwable e) {
			error = e;
			ConsistencyLogger.error(context, "取消阶段异常，后续开启异步重试，", e);
			// ignore
		}

		if (ConsistencyLogger.isDebugEnabled() && error == null && cancelPhaseResult) {
			ConsistencyLogger.debug(context, "取消阶段执行成功");
		} else if (ConsistencyLogger.isDebugEnabled() && !cancelPhaseResult) {
			ConsistencyLogger.debug(context, "取消阶段异常，部分参与者取消阶段未执行成功，后续开启异步重试，");
		}

		// 取消阶段需要抛出异常， 发起者不用处理，但是事务提供者需要将异常抛出器，抛给事务发起者
		if (error != null) {
			this.throwConsistencyException(error);
		}
	}

	/**
	 * 抛出一致性异常
	 * 
	 * @param error
	 */
	protected Object throwConsistencyException(Throwable error) {
		if (error != null && error instanceof ConsistencyException) {
			throw (ConsistencyException) error;
		}
		if (error.getCause() != null && error.getCause() instanceof ConsistencyException) {
			throw (ConsistencyException) error.getCause();
		}
		throw new ConsistencyException(error);
	}
}
